﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Globalization;
using System.Resources;
using Globsite.Globalization.Mvc.DbResources.Repositories;

namespace Globsite.Globalization.Mvc.DbResources
{
    public sealed class DbResourceManager : ResourceManager
    {
        private RepositoryBase resourceRepositoryField;
        private readonly Dictionary<string, DbResourceSet> resourceSets;
        private readonly object createResourceSyncLock;

        #region Ctors

        public DbResourceManager(string set)
        {
            Debug.WriteLine($"DbResourceManager.DbResourceManager({set})");

            this.BaseNameField = set;
            this.resourceSets = new Dictionary<string, DbResourceSet>();
            this.createResourceSyncLock = new object();
        }

        #region Removed for clarity

        //public DbResourceManager(Type resourceType)
        //    : this(resourceType.Namespace)
        //{
        //    Debug.WriteLine(string.Format("DbResourceManager.DbResourceManager({0})", resourceType));
        //}

        //public DbResourceManager(string baseName, Assembly assembly)
        //    : this(baseName)
        //{
        //    Debug.WriteLine(string.Format("DbResourceManager.DbResourceManager({0}, {1})", baseName, assembly));
        //}

        //public DbResourceManager(string baseName, Assembly assembly, Type usingResourceSet)
        //    : this(baseName)
        //{
        //    Debug.WriteLine(string.Format("DbResourceManager.DbResourceManager({0}, {1}, {2})", baseName, assembly, usingResourceSet));
        //}

        #endregion


        #endregion

        #region Overrides

        public override Type ResourceSetType
        {
            get
            {
                Debug.WriteLine("DbResourceManager.ResourceSetType_get");

                return typeof(DbResourceSet);
            }
        }

        public override void ReleaseAllResources()
        {
            Debug.WriteLine("DbResourceManager.ReleaseAllResources()");

            foreach (var keyValuePair in this.resourceSets)
                keyValuePair.Value.Close();
            this.resourceSets.Clear();

            base.ReleaseAllResources();
        }

        protected override ResourceSet InternalGetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents)
        {
            DbResourceSet resourceSet;
            if (!this.resourceSets.TryGetValue(culture.Name, out resourceSet))
            {
                lock (this.resourceSets)
                {
                    if (!this.resourceSets.TryGetValue(culture.Name, out resourceSet))
                    {
                        resourceSet = new DbResourceSet(this.BaseNameField, culture);
                        this.resourceSets.Add(culture.Name, resourceSet);
                    }
                }
            }

            return resourceSet;
        }

        public override string GetString(string name)
        {
            Debug.WriteLine($"DbResourceManager.GetString({name})");

            var value = base.GetString(name) ?? getValueForMissingResource(name);

            return value;
        }

        public override string GetString(string name, CultureInfo culture)
        {
            Debug.WriteLine($"DbResourceManager.GetString({name}, {culture})");

            var value = base.GetString(name, culture) ?? getValueForMissingResource(name, culture);

            return value;
        }

        public override object GetObject(string name)
        {
            Debug.WriteLine($"DbResourceManager.GetObject({name})");

            var value = (string)base.GetObject(name) ?? getValueForMissingResource(name);

            return value;
        }

        public override object GetObject(string name, CultureInfo culture)
        {
            Debug.WriteLine($"DbResourceManager.GetObject({name}, {culture})");

            var value = (string)base.GetObject(name, culture) ?? getValueForMissingResource(name, culture);

            return value;
        }

        #endregion

        private RepositoryBase resourceRepository
        {
            get { return this.resourceRepositoryField ?? (this.resourceRepositoryField = new RepositoryFactory().CreateRepository()); }
        }

        private void createResource(string name, string value)
        {
            // Przygotuj repozytorium.
            lock (this.createResourceSyncLock)
            {
                // Ponownie sprawdź czy w repozytorium istnieje dany zasób.
                var resource = this.resourceRepository.GetResource(name, this.BaseNameField, CultureInfo.InvariantCulture.Name);
                // Jeśli nie, to utwórz zasób dla kultury neutralnej.
                if (resource == null)
                    this.resourceRepository.AddResource(name, value, this.BaseNameField, CultureInfo.InvariantCulture.Name);
            }
        }

        private string getValueForMissingResource(string resourceKey, CultureInfo culture = null)
        {
            // Determine the resource value depending on configuration settings.
            var config = GlobsiteConfigSection.Instance.DbResources;
            string resourceValue;
            switch (config.MissingResourceValue)
            {
                case "${Null}":
                    resourceValue = null;
                    break;
                case "${StringEmpty}":
                    resourceValue = string.Empty;
                    break;
                default:
                    resourceValue = config.MissingResourceValue
                        .Replace("${ResourceSet}", this.BaseName)
                        .Replace("${ResourceKey}", resourceKey)
                        .Replace("${CultureCode}", culture?.Name ?? CultureInfo.CurrentUICulture.Name);
                    break;
            }
            // Create the new resource in a database.
            if (config.CreateResourceIfNotExists)
            {
                if (resourceValue == null)
                    throw new ConfigurationErrorsException("The null value is not valid if 'createResourceIfNotExists' option is active.");
                createResource(resourceKey, resourceValue);
            }

            return resourceValue;
        }
    }
}
